<h2>Why is this an issue?</h2>
<p><a href="https://learn.microsoft.com/en-us/dotnet/api/system.gc.collect">GC.Collect</a> is a method that forces or suggests to the <a
href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/">garbage collector</a> to run a collection of objects in the managed heap
that are no longer being used and free their memory.</p>
<p>Calling <code>GC.Collect</code> is rarely necessary and can significantly affect application performance. That’s because it is a <a
href="https://en.wikipedia.org/wiki/Tracing_garbage_collection">tracing garbage collector</a> and needs to examine <em>every object in memory</em> for
cleanup and analyze all reachable objects from every application’s root (static fields, local variables on thread stacks, etc.).</p>
<p>To perform tracing and memory releasing correctly, the garbage collection <a
href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/latency">may</a> need to block all threads currently in execution. That is
why, as a general rule, the <a
href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/performance#troubleshoot-performance-issues">performance implications</a>
of calling <code>GC.Collect</code> far outweigh the benefits.</p>
<p>This rule raises an issue when any overload of <code>Collect</code> is invoked.</p>
<pre>
static void Main(string[] args)
{
  // ...
  GC.Collect();                              // Noncompliant
  GC.Collect(2, GCCollectionMode.Optimized); // Noncompliant
}
</pre>
<p>There may be exceptions to this rule: for example, you’ve just triggered some event that is unique in the run of your program that caused a lot of
long-lived objects to die, and you want to release their memory.</p>
<p>This rule also raises on <code>GC.GetTotalMemory</code> when <code>forceFullCollection</code> is true as it directly invokes
<code>GC.Collect</code>.</p>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/">Garbage collection</a> </li>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/api/system.gc.collect">GC.Collect</a> </li>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/api/system.gc.gettotalmemory">GC.GetTotalMemory</a> </li>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/latency">Garbage collection latency modes</a> </li>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/performance#troubleshoot-performance-issues">Garbage collection
  troubleshoot performance issues</a> </li>
</ul>
<h3>Benchmarks</h3>
<p>Each .NET runtime features distinct implementations, modes, and configurations for its garbage collector. The benchmark below illustrates how
invoking <code>GC.Collect()</code> can have opposite effects across different runtimes.</p>
<table>
  <colgroup>
    <col style="width: 20%;">
    <col style="width: 20%;">
    <col style="width: 20%;">
    <col style="width: 20%;">
    <col style="width: 20%;">
  </colgroup>
  <thead>
    <tr>
      <th>Runtime</th>
      <th>Collect</th>
      <th>Mean</th>
      <th>Standard Deviation</th>
      <th>Allocated</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><p>.NET 9.0</p></td>
      <td><p>False</p></td>
      <td><p>659.2 ms</p></td>
      <td><p>15.69 ms</p></td>
      <td><p>205.95 MB</p></td>
    </tr>
    <tr>
      <td><p>.NET 9.0</p></td>
      <td><p>True</p></td>
      <td><p>888.8 ms</p></td>
      <td><p>15.34 ms</p></td>
      <td><p>205.95 MB</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>False</p></td>
      <td><p>545.7 ms</p></td>
      <td><p>19.49 ms</p></td>
      <td><p>228.8 MB</p></td>
    </tr>
    <tr>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>True</p></td>
      <td><p>484.8 ms</p></td>
      <td><p>11.79 ms</p></td>
      <td><p>228.8 MB</p></td>
    </tr>
  </tbody>
</table>
<h4>Glossary</h4>
<ul>
  <li> Collect - if <code>True</code>, <code>GC.Collect()</code> is called in the middle of the allocation heavy <code>Benchmark()</code> method </li>
  <li> <a href="https://en.wikipedia.org/wiki/Arithmetic_mean">Mean</a> </li>
  <li> <a href="https://en.wikipedia.org/wiki/Standard_deviation">Standard Deviation</a> </li>
  <li> <a href="https://github.com/dotnet/BenchmarkDotNet/blob/master/docs/articles/configs/diagnosers.md">Allocated</a> </li>
</ul>
<p>The results were generated by running the following snippet with <a href="https://github.com/dotnet/BenchmarkDotNet">BenchmarkDotNet</a>:</p>
<pre>
class Tree
{
    public List&lt;Tree&gt; Children = new();
}

private void AppendToTree(Tree tree, int childsPerTree, int depth)
{
    if (depth == 0)
    {
        return;
    }
    for (int i = 0; i &lt; childsPerTree; i++)
    {
        var child = new Tree();
        tree.Children.Add(child);
        AppendToTree(child, childsPerTree, depth - 1);
    }
}

[Benchmark]
[Arguments(true)]
[Arguments(false)]
public void Benchmark(bool collect)
{
    var tree = new Tree();
    AppendToTree(tree, 8, 7);        // Create 8^7 Tree objects (2.097.152 objects) linked via List&lt;Tree&gt; Children
    GC.Collect();
    GC.Collect();                    // Move the objects to generation 2
    AppendToTree(new Tree(), 8, 6);  // Add some more memory preasure (8^6 262.144 objects) which can be collected right after this call
    tree = null;                     // Remove all references to the tree and its content. This freees up 8^7 Tree objects (2.097.152 objects)
    if (collect)
    {
        GC.Collect();                // Force GC to run and block until it finishes
    }
    AppendToTree(new Tree(), 3, 10); // Do some more allocations (3^10 = 59.049)
    AppendToTree(new Tree(), 4, 7);  // 4^10 = 1.048.576
    AppendToTree(new Tree(), 5, 7);  // 5^7 = 78.125
    GC.Collect();                    // Collect all the memory allocated in this method
}
</pre>
<p>Hardware configuration:</p>
<pre>
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update)
Intel Core Ultra 7 165H, 1 CPU, 22 logical and 16 physical cores
  [Host]               : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
  .NET 9.0             : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
</pre>

